import pandas as pd
import tkinter as tk
from tkinter import simpledialog
from tkinter import ttk
import tkinter.messagebox
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import matplotlib.pyplot as plt
import seaborn as sns


# MobilePhoneInfo类用于处理手机数据的各种查询和操作
class MobilePhoneInfo:  # 定义MobilePhoneInfo类
    def __init__(self):  # 初始化方法
        self.data = pd.read_csv('mobile_phone_data.csv')  # 读取csv文件
        self.data = self.data.drop(['number'], axis=1)  # 删除’number‘列
        self.data = self.data.dropna()  # 删除包含缺失值的行
        self.data['Brand'] = self.data['Brand'].str.strip().str.lower()  # 品牌名称大写化和去除空格
        self.brand_dict = {
            '三星': 'samsung',
            '小米': 'xiaomi',
            '华为': 'huawei'}  # 添加其他品牌的映射

    def get_brand_data(self, brand):  # 获取指定品牌的数据
        if brand is None:  # 如果品牌名为空
            return pd.DataFrame()  # 返回一个空的DataFrame
        if brand in self.brand_dict:  # 如果品牌在映射字典中
            brand = self.brand_dict[brand]  # 获取映射后的品牌名称
        brand = brand.lower()  # 将品牌名称转换为小写
        self.data['Brand'] = self.data['Brand'].str.lower()  # 将数据中的品牌名称转换为小写
        brand_data = self.data[self.data['Brand'] == brand]  # 获取指定品牌的数据
        return brand_data  # 返回指定品牌的数据

    def get_brand_ratings(self):  # 获取各品牌的平均评分
        average_ratings = self.data.groupby('Brand')['Rating'].mean()  # 计算各品牌的平均评分
        return average_ratings  # 返回各品牌的平均评分

    def get_brand_models_rating(self, brand):  # 获取指定品牌的各型号的平均评分
        if brand in self.brand_dict:  # 如果品牌在映射字典中
            brand = self.brand_dict[brand]  # 获取映射后的品牌名称
        brand = brand.lower()  # 将品牌名称转换为小写
        self.data['Brand'] = self.data['Brand'].str.lower()  # 将数据中的品牌名称转换为小写
        brand_data = self.data[self.data['Brand'] == brand]  # 获取指定品牌的数据
        average_ratings = brand_data.groupby('Model')['Rating'].mean()  # 计算指定品牌的各型号的平均评分
        sorted_ratings = average_ratings.sort_values(ascending=False)  # 对平均评分进行降序排序
        return sorted_ratings  # 返回指定品牌的各型号的平均评分

    def get_brand_frequency(self):  # 获取各品牌的数据占比
        brand_frequency = self.data['Brand'].value_counts(normalize=True)  # 计算各品牌的数据频率
        df_brand_frequency = pd.DataFrame(brand_frequency).reset_index()  # 将数据频率转换为DataFrame格式
        df_brand_frequency.columns = ['Brand', 'Frequency']  # 重命名列名
        return df_brand_frequency  # 返回各品牌的数据占比

    def get_rating_price_data(self):  # 获取评级和价格的数据
        rating_price_data = self.data[['Rating', 'Price']]  # 选择'Rating'和'Price'列
        return rating_price_data  # 返回评级和价格的数据

    def get_specscore_price_data(self):  # 获取基本规格分和价格的数据
        specscore_price_data = self.data[['Spec_score', 'Price']]  # 选择'Spec_score'和'Price'列
        return specscore_price_data  # 返回基本规格分和价格的数据

    def get_model_data(self, brand, model):  # 获取指定品牌和型号的数据
        if brand in self.brand_dict:  # 如果品牌在映射字典中
            brand = self.brand_dict[brand]  # 获取映射后的品牌名称
        brand = brand.lower()  # 将品牌名称转换为小写
        model = model.lower()  # 将型号名称转换为小写
        self.data['Brand'] = self.data['Brand'].str.lower()  # 将数据中的品牌名称转换为小写
        self.data['Model'] = self.data['Model'].str.lower()  # 将数据中的型号名称转换为小写
        model_data = self.data[(self.data['Brand'] == brand) & (self.data['Model'].str.contains(model))]  # 获取指定品牌和型号的数据
        return model_data  # 返回指定品牌和型号的数据


# Application类为GUI应用程序，基于Tkinter实现
class Application(tk.Tk):  # 定义Application类
    def __init__(self):  # 初始化方法
        tk.Tk.__init__(self)  # 调用父类的初始化方法
        self.title("手机数据查询系统")  # 设置窗口标题
        self.info = MobilePhoneInfo()  # 创建MobilePhoneInfo实例以便查询手机数据

        # 创建一个居中的框架
        frame = tk.Frame(self)
        frame.pack(expand=True)

        # 创建按钮
        self.button1 = tk.Button(frame, text="手机品牌数据占比图", command=self.show_brand_info, padx=20, pady=10)
        self.button1.pack(side='top', pady=10)  # 设置垂直方向上的间隔为10像素

        self.button2 = tk.Button(frame, text="手机评级对比", command=self.show_brand_ratings, padx=20, pady=10)
        self.button2.pack(side='top', pady=10)  # 设置垂直方向上的间隔为10像素

        self.button3 = tk.Button(frame, text="手机评级-价格散点图", command=self.show_rating_price, padx=20, pady=10)
        self.button3.pack(side='top', pady=10)  # 设置垂直方向上的间隔为10像素

        self.button4 = tk.Button(frame, text="基本规格分-价格散点图", command=self.show_specscore_price, padx=20,
                                 pady=10)
        self.button4.pack(side='top', pady=10)  # 设置垂直方向上的间隔为10像素

        self.button5 = tk.Button(frame, text="手机品牌所含型号", command=self.show_brand_models, padx=20, pady=10)
        self.button5.pack(side='top', pady=10)  # 设置垂直方向上的间隔为10像素

        self.button6 = tk.Button(frame, text="手机型号数据查询", command=self.show_model_info, padx=20, pady=10)
        self.button6.pack(side='top', pady=10)  # 设置垂直方向上的间隔为10像素

        self.exit_button = tk.Button(frame, text="退出", command=self.quit, padx=10, pady=5)
        self.exit_button.pack(side='bottom', pady=20)  # 设置垂直方向上的间隔为20像素

        self.geometry("400x500")  # 设置窗口大小

    def show_brand_info(self):  # 显示手机品牌数据占比图
        self.show_chart("手机品牌数据占比图", self.info.get_brand_frequency(), 'pie')  # 调用show_chart方法

    def show_brand_ratings(self):  # 显示手机评级对比图
        self.show_chart("手机评级对比", self.info.get_brand_ratings(), 'line')

    def show_rating_price(self):  # 显示手机评级-价格散点图
        rating_price_data = self.info.get_rating_price_data().copy()  # Get a copy of the data
        if rating_price_data['Price'].dtype != 'object':
            rating_price_data.loc[:, 'Price'] = rating_price_data['Price'].astype(str)
        rating_price_data.loc[:, 'Price'] = pd.to_numeric(rating_price_data['Price'].str.replace(',', ''),
                                                          errors='coerce')

        new_window = tk.Toplevel(self)  # 创建新窗口
        new_window.title("手机评级-价格散点图")  # 设置窗口标题
        new_window.geometry("1000x600")  # 设置窗口大小

        fig = Figure(figsize=(30, 10), dpi=100)  # 设置图形大小
        ax = fig.add_subplot(111)  # 添加子图

        sns.scatterplot(data=rating_price_data, x='Rating', y='Price', color='blue', ax=ax)  # 绘制散点图
        plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置中文显示
        ax.set_title('手机评级-价格散点图')  # 设置标题
        ax.set_xlabel('手机评级')  # 设置x轴标签
        ax.set_ylabel('价格')  # 设置y轴标签

        home_button = tk.Button(new_window, text="主页", command=new_window.destroy)  # 创建主页按钮
        home_button.pack(side='bottom')  # 将主页按钮放置在窗口的底部

        canvas = FigureCanvasTkAgg(fig, master=new_window)  # 创建画布
        canvas.draw()  # 绘制图形
        canvas.get_tk_widget().pack()  # 将画布放置在窗口中

    def show_specscore_price(self):  # 显示基本规格-价格散点图
        specscore_price_data = self.info.get_specscore_price_data().copy()  # Get a copy of the data
        if specscore_price_data['Price'].dtype != 'object':
            specscore_price_data.loc[:, 'Price'] = specscore_price_data['Price'].astype(str)
        specscore_price_data.loc[:, 'Price'] = pd.to_numeric(specscore_price_data['Price'].str.replace(',', ''),
                                                             errors='coerce')

        if specscore_price_data['Spec_score'].dtype != 'object':
            specscore_price_data.loc[:, 'Spec_score'] = specscore_price_data['Spec_score'].astype(str)
        specscore_price_data.loc[:, 'Spec_score'] = specscore_price_data['Spec_score'].str.replace(',', '').astype(
            float)

        new_window = tk.Toplevel(self)  # 创建新窗口
        new_window.title("基本规格分-价格散点图")  # 设置窗口标题
        new_window.geometry("1000x600")  # 设置窗口大小

        fig = Figure(figsize=(15, 10), dpi=120)  # 设置图形大小
        ax = fig.add_subplot(111)  # 添加子图

        sns.scatterplot(data=specscore_price_data, x='Spec_score', y='Price', color='blue', ax=ax)  # 绘制散点图
        ax.set_title('基本规格分-价格散点图')  # 设置标题
        ax.set_xlabel('基本规格分')  # 设置x轴标签
        ax.set_ylabel('价格')  # 设置y轴标签

        canvas = FigureCanvasTkAgg(fig, master=new_window)  # 创建画布

        home_button = tk.Button(new_window, text="主页", command=new_window.destroy)  # 创建主页按钮
        home_button.pack(side='bottom')  # 将主页按钮放置在窗口的底部

        canvas.draw()  # 绘制图形
        canvas.get_tk_widget().pack()  # 将画布放置在窗口中

    def show_brand_models(self):  # 显示手机品牌所含型号
        while True:  # 循环直到找到品牌为止
            brand = simpledialog.askstring("输入", "请输入你想要查询的手机品牌名：")  # 获取用户输入的品牌名
            if brand is None:  # 如果用户点击了"Cancel"按钮
                return  # 返回，不再执行后续的代码
            result = self.info.get_brand_data(brand)  # 调用get_brand_data方法获取特定品牌的数据
            if result.empty:  # 如果未找到该品牌或品牌名为空
                tkinter.messagebox.showinfo("提示", "未找到该品牌，请重新输入。")  # 提示未找到品牌
            else:  # 如果找到该品牌
                result = result.reset_index()  # 将Series对象转换为DataFrame对象
                result = result.drop(['index', 'Brand'], axis=1)  # 删除index和Brand列

                new_window = tk.Toplevel(self)  # 创建新窗口
                new_window.title("手机品牌所含型号信息")  # 设置窗口标题
                new_window.geometry("1200x700")  # 修改窗口大小

                frame = tk.Frame(new_window)  # 创建框架
                frame.pack()  # 将框架放置在窗口中

                tree = ttk.Treeview(frame, columns=list(result.columns), show='headings', height=len(result))  # 创建表格
                for column in result.columns:  # 遍历列名
                    tree.column(column, width=100, anchor='center')  # 设置列
                    tree.heading(column, text=column)  # 设置表头

                for index, row in result.iterrows():  # 遍历DataFrame对象
                    tree.insert('', 'end', values=tuple(row))  # 插入数据

                tree.pack(side='left')  # 设置表格位置

                home_button = tk.Button(new_window, text="主页", command=new_window.destroy)  # 创建主页按钮
                home_button.pack()  # 设置按钮位置
                break  # 找到品牌后跳出循环

    def show_chart(self, title, data, chart_type):  # 显示手机评分对比图表
        new_window = tk.Toplevel(self)  # 创建新窗口
        new_window.title(title)  # 设置窗口标题
        new_window.geometry("1200x600")  # 设置窗口大小
        plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置中文显示
        fig = Figure(figsize=(12, 10), dpi=120)  # 设置图形大小
        ax = fig.add_subplot(111)  # 添加子图

        if chart_type == 'pie':  # 如果图表类型为饼图
            ax.pie(data['Frequency'], labels=data['Brand'], autopct='%1.1f%%')  # 绘制饼图
        elif chart_type == 'line':  # 如果图表类型为折线图
            ax.plot(data.index, data.values)  # 绘制折线图
            ax.set_xlabel('品牌')  # 设置x轴标签
            ax.set_ylabel('评分')  # 设置y轴标签
            ax.set_title(title)  # 设置标题

        canvas = FigureCanvasTkAgg(fig, master=new_window)  # 创建画布
        toolbar = NavigationToolbar2Tk(canvas, new_window)  # 创建工具栏
        toolbar.update()  # 更新工具栏

        home_button = tk.Button(new_window, text="主页", command=new_window.destroy)  # 创建主页按钮
        home_button.pack(side='bottom')  # 设置按钮位置

        canvas.draw()  # 绘制图形
        canvas.get_tk_widget().pack()  # 将画布放置在窗口中

    def show_model_info(self):  # 显示手机型号数据信息
        while True:  # 循环直到用户输入正确的品牌名
            brand = simpledialog.askstring("输入", "请输入你想要查询的手机品牌名：")  # 获取用户输入的品牌名
            if brand is None:  # 如果用户点击了"Cancel"按钮
                return  # 返回，不再执行后续的代码
            if self.info.get_brand_data(brand).empty:  # 如果未找到该品牌或品牌名为空
                tkinter.messagebox.showinfo("提示", "未找到该品牌，请重新输入。")  # 提示未找到品牌
            else:  # 如果找到该品
                break  # 退出循环

        while True:  # 循环直到用户输入正确的型号名
            model = simpledialog.askstring("输入", "请输入你想要查询的手机型号：")  # 获取用户输入的型号名
            result = self.info.get_model_data(brand, model)  # 调用get_model_data方法获取特定型号的数据
            if result.empty:  # 如果未找到该型号
                tkinter.messagebox.showinfo("提示", "未找到该型号，请重新输入。")  # 提示未找到型号
            else:  # 如果找到该型号
                break  # 退出循环

        result = result.reset_index()  # 将Series对象转换为DataFrame对象    # 将Series对象转换为DataFrame对象
        result = result.drop(['index'], axis=1)  # 删除index列

        new_window = tk.Toplevel(self)  # 创建新窗口
        new_window.title("手机型号数据信息")  # 设置窗口标题
        # 窗口高度根据数据的来调整
        new_window.geometry("1200x" + str(100 + len(result) * 40))  # 设置窗口大小

        frame = tk.Frame(new_window)  # 创建框架
        frame.pack()  # 将框架放置在窗口中

        tree = ttk.Treeview(frame, columns=list(result.columns), show='headings', height=len(result))  # 创建表格
        for column in result.columns:  # 遍历列名
            tree.column(column, width=100, anchor='center')  # 设置列
            tree.heading(column, text=column)  # 设置表头

        for index, row in result.iterrows():  # 遍历DataFrame对象
            tree.insert('', 'end', values=tuple(row))  # 插入数据

        tree.pack(side='left')  # 设置表格位置

        home_button = tk.Button(new_window, text="主页", command=new_window.destroy, padx=10, pady=5)  # 创建主页按钮
        home_button.pack()  # 设置按钮位置

        # 在”主页“键下方10像素的位置增加"对比”键
        compare_button = tk.Button(new_window, text="对比",
                                   command=lambda: self.compare_models(tree, result, new_window), padx=10, pady=5)
        compare_button.pack(pady=10)  # 设置按钮位置
        # 选中一行数据，再点击对比，会出现和上方一样的输入手机品牌和型号框

    def compare_models(self, tree, result, new_window):  # 对比两个型号的数据
        selected_item = tree.selection()[0]  # 获取选中的行
        selected_data = tree.item(selected_item)['values']  # 获取选中行的数据

        while True:  # 循环直到用户输入正确的品牌名
            brand = simpledialog.askstring("输入", "请输入你想要查询的手机品牌名：")  # 获取用户输入的品牌名
            if brand is None:  # 如果用户点击了"Cancel"按钮
                return  # 返回，不再执行后续的代码
            if self.info.get_brand_data(brand).empty:  # 如果未找到该品牌或品牌名为空
                tkinter.messagebox.showinfo("提示", "未找到该品牌，请重新输入。")  # 提示未找到品牌
            else:  # 如果找到该品牌
                break  # 退出循环

        while True:  # 循环直到用户输入正确的型号名
            model = simpledialog.askstring("输入", "请输入你想要查询的手机型号：")  # 获取用户输入的型号名
            result = self.info.get_model_data(brand, model)  # 调用get_model_data方法获取特定型号的数据
            if result.empty:  # 如果未找到该型号
                tkinter.messagebox.showinfo("提示", "未找到该型号，请重新输入。")  # 提示未找到型号
            else:  # 如果找到该型号
                break  # 退出循环

        # 对比selected_data和result的所有字段
        comparison_result = []  # 用于存储对比结果
        for i, column in enumerate(result.columns):  # 遍历所有字段
            selected_value = selected_data[i]  # 获取选中的数据
            result_value = result[column].values[0]  # 获取对比型号的数据
            comparison_result.append([column, selected_value, result_value])  # 将对比结果添加到列表中

        # 创建新窗口来显示对比结果
        comparison_window = tk.Toplevel(new_window)  # 创建新窗口
        comparison_window.title("对比结果")  # 设置窗口标题
        comparison_window.geometry("1000x500")  # 设置窗口大小

        # 创建表格
        comparison_tree = ttk.Treeview(comparison_window, columns=["字段", "选中的数据", "对比型号的数据"],
                                       show='headings')
        comparison_tree.column("字段", width=200, anchor='center')
        comparison_tree.column("选中的数据", width=300, anchor='center')
        comparison_tree.column("对比型号的数据", width=300, anchor='center')
        comparison_tree.heading("字段", text="字段")
        comparison_tree.heading("选中的数据", text="选中的数据")
        comparison_tree.heading("对比型号的数据", text="对比型号的数据")
        comparison_tree.pack(fill='both', expand=True)
        # 设置tag
        comparison_tree.tag_configure('oddrow', background='lightblue')
        comparison_tree.tag_configure('oddcolumn', background='lightblue')

        # 插入数据
        for row in comparison_result:
            comparison_tree.insert('', 'end', values=row)

        comparison_tree.pack()

        home_button = tk.Button(comparison_window, text="关闭", command=comparison_window.destroy)
        home_button.pack()


def exit_program(self):
    # Add any operations you want to perform before exiting here
    self.quit()


if __name__ == "__main__":  # 主函数
    app = Application()  # 创建Application对象
    app.mainloop()  # 运行主循环
